#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include "ohos_init.h"
#include "cmsis_os2.h"
#include "wifi_device.h"
#include "lwip/netifapi.h"
#include "lwip/api_shell.h"
#include "lwip/sockets.h"

static int g_scanDone = 0;
static int g_connected = 0;
static int g_netId = -1;

static char* SecurityTypeName(WifiSecurityType type)
{
    switch (type)
    {
    case WIFI_SEC_TYPE_OPEN:
        return "OPEN";
    case WIFI_SEC_TYPE_WEP:
        return "WEP";
    case WIFI_SEC_TYPE_PSK:
        return "PSK";
    case WIFI_SEC_TYPE_SAE:
        return "SAE";
    default:
        break;
    }
    return "unkow";
}

static void PrintLinkedInfo(WifiLinkedInfo* info)
{
    if (!info) return;

    static char macAddress[32] = {0};
    unsigned char* mac = info->bssid;
    snprintf(macAddress, sizeof(macAddress), "%02X:%02X:%02X:%02X:%02X:%02X",
        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    printf("bssid: %s, rssi: %d, connState: %d, reason: %d, ssid: %s\r\n",
        macAddress, info->rssi, info->connState, info->disconnectedReason, info->ssid);
}
static void OnWifiConnectionChanged(int state, WifiLinkedInfo* info)
{
    if (!info) return;

    printf("%s %d, state = %d, info = \r\n", __FUNCTION__, __LINE__, state);
    PrintLinkedInfo(info);

    if (state == WIFI_STATE_AVALIABLE) {
        g_connected = 1;
    } else {
        g_connected = 0;
    }
}

void PrintScanResult(void)
{
    WifiScanInfo scanResult[WIFI_SCAN_HOTSPOT_LIMIT] = {0};
    uint32_t resultSize = WIFI_SCAN_HOTSPOT_LIMIT;

    memset(&scanResult, 0, sizeof(scanResult));
    WifiErrorCode errCode = GetScanInfoList(scanResult, &resultSize);
    if (errCode != WIFI_SUCCESS) {
        printf("GetScanInfoList failed: %d\r\n", errCode);
    }
    for (uint32_t i = 0; i < resultSize; i++) {
        static char macAddress[32] = {0};
        WifiScanInfo info = scanResult[i];
        unsigned char* mac = info.bssid;
        snprintf(macAddress, sizeof(macAddress), "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
        printf("Result[%d]: %s, % 4s, %d, %d, %d, %s\r\n", i, macAddress, SecurityTypeName(info.securityType), info.rssi, info.band, info.frequency, info.ssid);
    }
}



void OnWifiScanStateChanged(int state, int size)
{
    printf("%s %d, state = %X, size = %d\r\n", __FUNCTION__, __LINE__, state, size);

    if (state == WIFI_STATE_AVALIABLE && size > 0) {
        g_scanDone = 1;
    }
}

WifiEvent g_eventListener = {
    .OnWifiConnectionChanged = OnWifiConnectionChanged,
    .OnWifiScanStateChanged = OnWifiScanStateChanged
};
void WifiConnect(void )
{
    WifiErrorCode errCode;
    WifiDeviceConfig apConfig = {};

    osDelay(10);
    errCode = RegisterWifiEvent(&g_eventListener);
    printf("RegisterWifiEvent: %d\r\n", errCode);

    // setup your AP params
    strcpy(apConfig.ssid, "Yourssid");
    strcpy(apConfig.preSharedKey, "Yourpasswd");
    apConfig.securityType = WIFI_SEC_TYPE_PSK;

    errCode = EnableWifi();
    printf("EnableWifi: %d\r\n", errCode);
    osDelay(100);

    g_scanDone = 0;
    errCode = Scan();
    printf("Scan: %d\r\n", errCode);
    

    // wait for scan done!
    while (!g_scanDone) {
        osDelay(5);
    }
    PrintScanResult();

    errCode = AddDeviceConfig(&apConfig, &g_netId);
    printf("AddDeviceConfig: %d\r\n", errCode);

    g_connected = 0;
    errCode = ConnectTo(g_netId);
    printf("ConnectTo(%d): %d\r\n", g_netId, errCode);

    while (!g_connected) {
        osDelay(10);
    }
    printf("g_connected: %d\r\n", g_connected);
    osDelay(50);

    // 联网业务开始
    // 这里是网络业务代码...
    struct netif* iface = netifapi_netif_find("wlan0");
    if (iface) {
        err_t ret = netifapi_dhcp_start(iface);
        printf("netifapi_dhcp_start: %d\r\n", ret);

        osDelay(200); // wait DHCP server give me IP
        ret = netifapi_netif_common(iface, dhcp_clients_info_show, NULL);
        printf("netifapi_netif_common: %d\r\n", ret);
    }

   


}

void WifiDisconnet(void )
{
    WifiErrorCode errCode;
            
    Disconnect(); // disconnect with your AP  
    printf("Disconnect ok");      
    RemoveDevice(g_netId); // remove AP config        
    errCode = DisableWifi();        
    printf("DisableWifi: %d\r\n", errCode);
    osDelay(500);
    

}
static char request[128] = "";
static char response[128] = "";
unsigned short HoldingReg[16] = {1,2,3,4,5,6,7,8,9,8,8,0,5,2,5,7};

//#define MBAP_SIZE (7)
//#define MB_WRMULREG_REQ_SIZE (6)
//#define MB_WRMULREG_RES_SIZE (5)
//#define MB_RDMULREG_REQ_SIZE (5)
//#define MB_RDMULREG_RES_SIZE (2)

#pragma pack (1)
struct mbap{
    //mbap
    unsigned short tranid;//传输id  关联请求和响应
    unsigned short pid;  //pid=0;
    unsigned short len;//number of following byetes
    unsigned char uid; //1~247   当一个server作为路由连接串口modbus时，这个变量表示从机地址
    unsigned char pdu[0];
};


struct mb_wrmulreg_request{
    unsigned char fun_code;//0x10
    unsigned char start_hi;//0x0000 to 0xFFFF
    unsigned char start_lo;
    unsigned char quan_hi;//0x0001 to 0x007B (123)
    unsigned char quan_lo;
    unsigned char byte_count;//2*N
    //Register value
    unsigned char regs[0];   //GUN C
};

struct mb_wrmulreg_response{
    unsigned char fun_code;//0x10
    unsigned char start_hi;//0x0000 to 0xFFFF
    unsigned char start_lo;
    unsigned char quan_hi;//0x0001 to 0x007B (123)
    unsigned char quan_lo;
};

struct mb_rdmulreg_request{
    unsigned char fun_code;//0x03
    unsigned char start_hi;//0x0000 to 0xFFFF
    unsigned char start_lo;
    unsigned char quan_hi;//0x0001 to 0x007D (125)
    unsigned char quan_lo;
};

struct mb_rdmulreg_response{
    unsigned char fun_code;//0x03
    unsigned char byte_count;//2*N
    //Register value
    unsigned char regs[0];   //GUN C
};

#pragma pack ()


static int parse_mbap(struct mbap *mbap)
{
    if(NULL == mbap)
        return -1;

    if(mbap->pid != 0)
        return -1;

    return 0;
}
/*
static int parse_pdu(void)
{
 
        return 0x81;//Invalid function
    
}
*/


int TcpServer(void)
{
    int retval = 0;
    int sockfd = -1;
    int connfd = -1;

    struct sockaddr_in clientAddr = {0};
    socklen_t clientAddrLen = sizeof(clientAddr);
    struct sockaddr_in serverAddr = {0};
    int backlog = 1;//取决于调用accept调用的是否及时


    printf("struct mbap = [%d] \r\n",sizeof(struct mbap));


    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		printf("socket error");
		return -1;
	}

    serverAddr.sin_family = AF_INET;
    //服务器的端口号
	serverAddr.sin_port = htons(502);
	//写入服务器地址
	serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); // 允许任意主机接入， 0.0.0.0

    retval = bind(sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)); // 绑定端口
    if (retval < 0) {
        printf("bind failed, %ld!\r\n", retval);
        goto do_cleanup;
    }
    printf("bind to port %d success!\r\n",502);


    retval = listen(sockfd, backlog); // 开始监听
    if (retval < 0) {
        printf("listen failed!\r\n");
        goto do_cleanup;
    }
    printf("listen with %d backlog success!\r\n", backlog);

    // 接受客户端连接，成功会返回一个表示连接的 socket ， clientAddr 参数将会携带客户端主机和端口信息 ；失败返回 -1
    // 此后的 收、发 都在 表示连接的 socket 上进行；之后 sockfd 依然可以继续接受其他客户端的连接，
    //  UNIX系统上经典的并发模型是“每个连接一个进程”——创建子进程处理连接，父进程继续接受其他客户端的连接
    //  鸿蒙liteos-a内核之上，可以使用UNIX的“每个连接一个进程”的并发模型
    //     liteos-m内核之上，可以使用“每个连接一个线程”的并发模型
    connfd = accept(sockfd, (struct sockaddr *)&clientAddr, &clientAddrLen);
    if (connfd < 0) {
        printf("accept failed, %d, %d\r\n", connfd, errno);
        goto do_cleanup;
    }
    printf("accept success, connfd = %d!\r\n", connfd);
    printf("client addr info: host = %s, port = %d\r\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));

    // 后续 收、发 都在 表示连接的 socket 上进行；


    while(1)
    {
        
         
        retval = recv(connfd, request, sizeof(struct mbap), 0);
        if (retval < 0) {
            printf("recv request failed, %ld!\r\n", retval);
            goto do_disconnect;
        }
          
        
        printf("recv read [%d] bytes!\r\n", retval);
        retval = 0;

        if(parse_mbap((struct mbap *)request)<0){
            printf("parse_mbap  error\r\n");
        }
       
        ((struct mbap *)response)->pid = ((struct mbap *)request)->pid;
        ((struct mbap *)response)->tranid = ((struct mbap *)request)->tranid;
        ((struct mbap *)response)->uid = ((struct mbap *)request)->uid;

        unsigned char * pregs = NULL;

        retval = recv(connfd, &request[sizeof(struct mbap)], ((struct mbap *)request)->len-1, 0);
        if (retval < 0) {
            printf("recv request failed, %ld!\r\n", retval);
            goto do_disconnect;
        }
        if(((struct mbap *)request)->pdu[0] == 0x3){
            unsigned short start;
            unsigned short quan;
            struct mb_rdmulreg_request * prequest = (struct mb_rdmulreg_request *)&request[sizeof(struct mbap)];

            start = (prequest->start_hi<<16)|(prequest->start_lo);
            quan = (prequest->quan_hi<<16)|(prequest->quan_lo);
            if(start+quan>sizeof(HoldingReg)){
                prequest->fun_code = prequest->fun_code+0x80;

            }
            else
            {
                struct mb_rdmulreg_response * presponse = (struct mb_rdmulreg_response *)&response[sizeof(struct mbap)];
                presponse->fun_code = prequest->fun_code;
                presponse->byte_count = quan * 2;
                pregs = (unsigned char *)&response[sizeof(struct mbap)+sizeof(struct mb_rdmulreg_response)];
                memcpy(pregs,&HoldingReg[start],presponse->byte_count);
                ((struct mbap *)response)->len = 1 + sizeof(struct mb_rdmulreg_response) + presponse->byte_count;

            }

        }
        else if(((struct mbap *)request)->pdu[0] == 0x10){


            unsigned short start;
            unsigned short quan;
            struct mb_wrmulreg_request * prequest = (struct mb_wrmulreg_request *)&request[sizeof(struct mbap)];

            start = (prequest->start_hi<<16)|(prequest->start_lo);
            quan = (prequest->quan_hi<<16)|(prequest->quan_lo);

            if(start+quan>sizeof(HoldingReg)){
                prequest->fun_code = prequest->fun_code+0x80;

            }
            else
            {
                struct mb_wrmulreg_response * presponse = (struct mb_wrmulreg_response *)&response[sizeof(struct mbap)];

                presponse->fun_code = prequest->fun_code;
                presponse->start_hi = prequest->start_hi;
                presponse->start_lo = prequest->start_lo;
                presponse->quan_hi = prequest->quan_hi;
                presponse->quan_lo = prequest->quan_lo;

                pregs = &prequest->regs[0];
                memcpy(&HoldingReg[start],pregs,prequest->byte_count);
                ((struct mbap *)response)->len = 1 + sizeof(struct mb_wrmulreg_response) ;

            }
        }
        


        retval = send(connfd, response, ((struct mbap *)response)->len + sizeof(struct mbap) - 1, 0);
        if (retval <= 0) {
            printf("send response failed, %ld!\r\n", retval);
            goto do_disconnect;
        }
        printf("send response{%d} to client done!\r\n", retval);
        for(int i=0;i<retval;i++)
        {
            printf("%x ",response[i]);
        }
        printf("\r\n");
        retval = 0;

    }
    

do_disconnect:
    sleep(1);
    closesocket(connfd);
    sleep(1); // for debug

do_cleanup:
    printf("do_cleanup...\r\n");

    closesocket(sockfd);

    
    return 0;


}







static void ModbusTCPTask(void *arg)
{
    (void)arg;
    
    WifiConnect();
    TcpServer();


}


static void ModbusTCP(void)
{
    osThreadAttr_t attr;

    attr.name = "ModbusTCPTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 10240;
    attr.priority = osPriorityNormal;

    if (osThreadNew(ModbusTCPTask, NULL, &attr) == NULL) {
        printf("[ModbusTCP] Falied to create ModbusTCPTask!\n");
    }
}

APP_FEATURE_INIT(ModbusTCP);

